package lingou.homework.postbar.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lingou.homework.postbar.dao.*;
import lingou.homework.postbar.domain.*;
import lingou.homework.postbar.exception.BusinessException;
import lingou.homework.postbar.exception.SystemException;
import lingou.homework.postbar.service.enums.MessageOpEnum;
import lingou.homework.postbar.service.enums.PostTypeEnum;
import lingou.homework.postbar.service.tool.Tools;
import lingou.homework.postbar.tool.StaticURL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Timestamp;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

@Service
public class PostService {
    @Autowired
    PostMapper postMapper;
    @Autowired
    PubPostMapper pubPostMapper;
    @Autowired
    TagMapper tagMapper;
    @Autowired
    PostSnapshotMapper postSnapshotMapper;
    @Autowired
    NewsPostMapper newsPostMapper;
    @Autowired
    NoticePostMapper noticePostMapper;
    @Autowired
    CommentMapper commentMapper;
    @Autowired
    BoxMapper boxMapper;
    @Autowired
    LikeOperationMapper likeOperationMapper;
    @Autowired
    MessageMapper messageMapper;
    @Autowired
    UserMapper userMapper;
    @Autowired
    PostTagMapper postTagMapper;

    public boolean isVisible(long userId, long postId){
        // TODO
        return true;
    }

    public List<Post> getPublicPosts(int start, Long count, long[] tags, String search, Long userID, boolean draw) {
        String p = null;
//        if(search != null && search.length() != 0) p = ".*" + Pattern.quote(search) + ".*";
        // FIXME: 转义
        if(search != null && search.length() != 0) p = "%" + search + "%";
        return pubPostMapper.getPublicPosts(start, count, p, tags, userID, draw);
    }

    public List<Post> getNewsPosts(int start, Integer count){
        if(count == null) return newsPostMapper.selectList(null).stream()
                .map(x -> getPost(x.getPostId())).toList();
        return newsPostMapper.getPosts(start, count);
    }

    public List<Post> getNoticePosts(int start, Integer count){
        if(count == null) return noticePostMapper.selectList(null).stream()
                .map(x -> getPost(x.getPostId())).toList();
        return noticePostMapper.getPosts(start, count);
    }

    public List<Tag> getTags(long opUser, long postId){
        if(!isVisible(opUser, postId)) throw BusinessException.NO_PERMISSION;
        return postTagMapper.selectList(new QueryWrapper<PostTag>().eq("postId", postId)).stream()
                .map(postTag -> tagMapper.selectById(postTag.getTagId())).toList();
    }

    public void setTags(long opUser, long postId, long[] tags){
        if(getPost(postId).getUserId() != opUser) throw BusinessException.NO_PERMISSION;
        QueryWrapper<PostTag> queryWrapper = new QueryWrapper<PostTag>()
                .eq("postId", postId);
        postTagMapper.delete(queryWrapper);
        if(tags == null) return;
        for (long tag : tags) {
            postTagMapper.insert(new PostTag(postId, tag));
        }
    }

    public List<Tag> getAllPostTags(){
        return tagMapper.selectList(null);
    }

    public PostSnapshot getContent(long postId){
        Post post = getPost(postId);
        Long snapshotId = post.getSnapshotId();
        if(snapshotId == null) {
            PostSnapshot postSnapshot = new PostSnapshot();
            postSnapshot.setContent(post.getPreview());
            postSnapshot.setTime(post.getTime());
            return postSnapshot;
        }
        return postSnapshotMapper.selectById(snapshotId);
    }

    public Post getPost(long postId){
        return postMapper.selectById(postId);
    }

    public void deletePost(long postId){
        //TODO: message
//        Message entity = new Message();
//        messageMapper.insert(entity)
        postMapper.deleteById(postId);

    }

    public boolean isPublicPost(long postId){
        return pubPostMapper.selectById(postId) != null;
    }

    @Transactional
    public Post publishPost(long userId, String title, String content, boolean anno, boolean draw){
        Post post = new Post();
        post.setAnon(anno ? 1 : 0);
        post.setDraw(draw ? 1 : 0);
        post.setType((long) PostTypeEnum.PUB.ordinal());
        genPost(userId, title, content, post);

        PubPost entity = new PubPost();
        entity.setPostId(post.getPostId());
        pubPostMapper.insert(entity);

        return post;
    }

    @Transactional
    public void postNotice(long userId, String title, String content){
        Post post = new Post();
        post.setType((long) PostTypeEnum.NOTICE.ordinal());
        genPost(userId, title, content, post);

        NoticePost entity = new NoticePost();
        entity.setPostId(post.getPostId());

        noticePostMapper.insert(entity);

        List<User> users = userMapper.selectList(null);
        users.forEach(user -> {
            System.out.println(user.getUserId());
            if(user.getUserId() != userId)
                messageMapper.insert(new Message(user.getUserId(), entity.getPostId(), 0L, MessageOpEnum.NOTICE.getId(), null));
        });

    }

    @Transactional
    public void postNews(long userId, String title, String content){
        Post post = new Post();
        post.setType((long) PostTypeEnum.NEWS.ordinal());
        genPost(userId, title, content, post);

        NewsPost entity = new NewsPost();
        entity.setPostId(post.getPostId());

        newsPostMapper.insert(entity);
    }

    @Transactional
    public void modifyPost(long postId, String title, String content){
        Post post = getPost(postId);
        if(post.getSnapshotId() == null) throw BusinessException.NO_PERMISSION;
        PostSnapshot postsnapshot = new PostSnapshot();
        genSnapshot(title, content, post.getSnapshotId(), postsnapshot);
        post.setSnapshotId(postsnapshot.getSnapshotId());
        post.setPreview(title);
        postMapper.updateById(post);
    }

    public void setPostDraw(long postId, boolean draw){
        Post post = new Post();
        post.setPostId(postId);
        post.setDraw(draw ? 1 : 0);
        postMapper.updateById(post);
    }

    @Transactional
    public long reply(long postId, long userId, String content, boolean anno, Long atUserId){
        Post post = getPost(postId);
        Post newPost = new Post();
        Message message = new Message();
        message.setMsgOp(1L);
        if(atUserId != null) {
//            User user = userMapper.selectById(atUserId);
//            if(user != null) {
//                String userName = user.getUserName();
//                content = "回复&nbsp;" + Tools.genAt(user) + "&nbsp;: " + content;
//            }
            message.setUserId(atUserId);
        }
        else {
            message.setUserId(post.getUserId());
        }
        newPost.setAnon(anno ? 1 : 0);
//        genPost(userId, null, content, post);
        newPost.setType((long) PostTypeEnum.COMMENT.ordinal());
        genPostWithoutSnapshot(userId, content, newPost);
        message.setPostId(newPost.getPostId());
        message.setStatus(0L);

        Comment comment = new Comment();
        comment.setFatherId(postId);
        comment.setChildId(newPost.getPostId());
        comment.setAtUserId(atUserId);
        commentMapper.insert(comment);

        if(post.getUserId() != userId)
            messageMapper.insert(message);

        return newPost.getPostId();
    }

    public List<Post> getAllReply(long postId){
        List<Comment> all = commentMapper.selectList(new QueryWrapper<Comment>().eq("fatherID", postId));
        return all.stream().map(comment -> getPost(comment.getChildId())).toList();
    }

    public Post getTopPost(long postId){
        while (true){
            Post fatherPost = getFatherPost(postId);
            if(fatherPost == null) return getPost(postId);
            postId = fatherPost.getPostId();
        }
    }

    public Post getFatherPost(long postId){
        Comment comment = commentMapper.selectById(postId);
        if(comment == null) return null;
        return getPost(comment.getFatherId());
    }

//    public int getReplyCount(long postId){
//        return getAllReply(postId).size();
//    }

    @Transactional
    public List<Post> getBoxPosts(Long userId, boolean pub, Boolean hasAnswer, int start, Long count){
        return boxMapper.getBoxPosts(userId, start, count, pub, hasAnswer);
    }

    public int getBoxPostCount(long userId, boolean pub){
        List<Post> boxPosts = boxMapper.getBoxPosts(userId, 0, null, pub, null);
        return boxPosts.size();
    }

    public long getBoxOwner(long postId) {
        Box box = boxMapper.selectById(postId);
        if(box == null) throw BusinessException.UNSUPPORTED;
        return box.getUserId();
    }

    @Transactional
    public long postQuestion(long userId, long ownerId, String title, String content){
        //TODO: message
        if(userId == ownerId) throw new BusinessException("不能向自己提问！");
        Post post = new Post();
        post.setAnon(1);
        post.setType((long) PostTypeEnum.BOX.ordinal());
        genPost(userId, title, content, post);
        Box box = new Box();
        box.setPostId(post.getPostId());
        box.setUserId(ownerId);
        box.setPub(0);
        boxMapper.insert(box);

        Message message = new Message(ownerId, post.getPostId(), 0L, 2L, null);
        messageMapper.insert(message);
        return post.getPostId();
    }

    public long replyBoxPost(long userId, long postId, String content, boolean pub) {
        //TODO: message
        Box box = boxMapper.selectById(postId);
        if(box == null) throw BusinessException.UNSUPPORTED;
        Post post = postMapper.selectById(postId);
        boolean secondAsk = userId == getPost(postId).getUserId(); // 提问者追问
        boolean owner = box.getUserId().equals(userId);
        if(pub){
            if(!owner) throw BusinessException.NO_PERMISSION;
            setPubAnswer(userId, postId, true);
        }
        if(!owner && !post.getUserId().equals(userId)) throw BusinessException.NO_PERMISSION;
        long l = reply(postId, userId, content, !owner, null);
        if(secondAsk) {
            // 如果是提问者追问，则通知提问箱所有者
            messageMapper.insert(new Message(box.getUserId(), l, 0L, 1L, null));
        }
        return l;
    }

    public boolean setPubAnswer(long userId, long postId, boolean pub){
        Box box = new Box();
        box.setPostId(postId);
        box.setPub(pub ? 1 : 0);
        return this.boxMapper.updateById(box) > 0;
    }

    public boolean isBoxPostPub(long userId, long postId){
        Box box = boxMapper.selectById(postId);
        if(box == null) return false;
        return box.getPub() > 0;
    }

    /**
     * 点赞相关操作
     * @param userId 操作用户ID
     * @param postId 帖子ID
     * @param undo 是否是撤销操作
     * @param op 0为点赞，1为点踩
     * @return 返回操作是否成功
     */
    @Transactional
    public void opLikePost(long userId, long postId, boolean undo, long op){
        //TODO: message
        if(getPost(postId).getUserId().equals(userId)) throw new BusinessException("不能对自己的帖子进行此操作！");
        if(!isVisible(userId, postId)) throw BusinessException.NO_PERMISSION;
        if(undo){
            // 撤销操作
            if(likeOperationMapper.deleteByOp(userId, postId, op) > 0){
                if(op == 0) postMapper.addLike(postId, -1);
                else postMapper.addDislike(postId, -1);
            }
            else throw new BusinessException("无法撤销！");
        }
        else {
            LikeOperation entity = likeOperationMapper.get(userId, postId);
            if(entity == null) {
                entity = new LikeOperation();
                entity.setPostId(postId);
                entity.setUserId(userId);
                entity.setOperation(op);
                if(likeOperationMapper.insert(entity) > 0) {
                    if(op == 0) postMapper.addLike(postId, 1);
                    else postMapper.addDislike(postId, 1);
                }
                else throw SystemException.FAILED;
            }
            else {  // 已经存在操作
                if(entity.getOperation() != op) {
                    if(likeOperationMapper.setOp(userId, postId, op) > 0){
                        if(op == 0) {
                            postMapper.addDislike(postId, -1);
                            postMapper.addLike(postId, 1);
                        }
                        else {
                            postMapper.addLike(postId, -1);
                            postMapper.addDislike(postId, 1);
                        }
                    }
                    else throw SystemException.FAILED;
                }
                else throw BusinessException.REPEATED;
                // 若已经进行过操作，则不进行
            }
        }
    }

    public boolean isOpLiked(long userId, long postId, int op){
        LikeOperation likeoperation = likeOperationMapper.get(userId, postId);
        if(likeoperation == null) return false;
        return likeoperation.getOperation() == op;
    }

    @Transactional
    public List<Message> getMessages(int start, Integer count, boolean unread, long userId){
        genLikeMessages(userId);
        return messageMapper.getMessages(start, count, unread, userId);
    }

    public void readAllMessages(long userId){
        messageMapper.readAll(userId);
    }

    public void readMessage(long userId, long messageId){
        Message message = messageMapper.selectById(messageId);
        if(message.getUserId() != userId) throw BusinessException.NO_PERMISSION;
        message.setStatus(1L);
        messageMapper.updateById(message);
    }

    private void genLikeMessages(long userId){
        List<LikeOperation> likeOperations = likeOperationMapper.selectList(new QueryWrapper<LikeOperation>()
                .eq("noticed", 0L));
        likeOperations = likeOperations.stream().filter(likeOperation -> {
            Post post = getPost(likeOperation.getPostId());
            return post.getUserId() == userId;
        }).toList();
        HashMap<Long, Long> postMap = new HashMap<>();
        likeOperations.forEach(x -> {
            Long i = postMap.get(x.getPostId());
            if(i == null) i = 1L;
            else i++;
            postMap.put(x.getPostId(), i);
            likeOperationMapper.setNoticed(x.getUserId(), x.getPostId(), 1L);
        });
        postMap.forEach((postId, i) -> {
            messageMapper.insert(new Message(userId, postId, 0L, 0L, i));
        });
    }

    private void genPost(long userId, String title, String content, Post post){
        PostSnapshot postsnapshot = new PostSnapshot();
        genSnapshot(title, content, null, postsnapshot);
        post.setUserId(userId);
        post.setTime(postsnapshot.getTime());
        post.setSnapshotId(postsnapshot.getSnapshotId());
        post.setPreview(title);
        postMapper.insert(post);
    }

    private void genPostWithoutSnapshot(long userId, String content, Post post) {
        post.setUserId(userId);
        post.setTime(Tools.getNow());
        post.setPreview(content);
        post.setSnapshotId(null);
        postMapper.insert(post);
    }

    private void genSnapshot(String title, String content, Long last, PostSnapshot newp){
        Timestamp now = Tools.getNow();
        newp.setTime(now);
        newp.setContent(content);
        newp.setTitle(title);
        newp.setLastId(last);
        postSnapshotMapper.insert(newp);
    }

}
